import org.eclipse.milo.opcua.sdk.client.OpcUaClient;
import org.eclipse.milo.opcua.sdk.client.api.identity.AnonymousProvider;
import org.eclipse.milo.opcua.stack.core.AttributeId;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.*;
import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.UInteger;
import org.eclipse.milo.opcua.stack.core.types.enumerated.MonitoringMode;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoredItemCreateRequest;
import org.eclipse.milo.opcua.stack.core.types.structured.MonitoringParameters;
import org.eclipse.milo.opcua.stack.core.types.structured.ReadValueId;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;


/**
 * 无密码无证书无安全认证模式
 * @Author: majinzhong
 * @Data:2024/8/30
 */
public class OpcUAClient {
    //opc ua服务端地址
    private final static String endPointUrl = "opc.tcp://LshHwNotebook:53530/OPCUA/SimulationServer";
//    private final static String endPointUrl = "opc.tcp://192.168.24.13:4840";

    public static void main(String[] args) {
        try {
            //创建OPC UA客户端
            OpcUaClient opcUaClient = createClient();

            //开启连接
            opcUaClient.connect().get();
            // 订阅消息
            subscribe(opcUaClient);

            // 写入
            writeValue(opcUaClient);

            // 读取
            readValue(opcUaClient);

            Thread.sleep(10000);

            // 关闭连接
            opcUaClient.disconnect().get();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 创建OPC UA客户端
     *
     * @return
     * @throws Exception
     */
    private static OpcUaClient createClient() throws Exception {
        Path securityTempDir = Paths.get(System.getProperty("java.io.tmpdir"), "security");
        Files.createDirectories(securityTempDir);
        if (!Files.exists(securityTempDir)) {
            throw new Exception("unable to create security dir: " + securityTempDir);
        }
        return OpcUaClient.create(endPointUrl,
                endpoints ->
                        endpoints.stream()
                                .filter(e -> e.getSecurityPolicyUri().equals(SecurityPolicy.None.getUri()))
                                .findFirst(),
                configBuilder ->
                        configBuilder
                                .setApplicationName(LocalizedText.english("OPC UA test")) // huazh-01
                                .setApplicationUri("urn:eclipse:milo:client") // ns=2:s=huazh-01.device1.data-huazh
                                //访问方式 new AnonymousProvider()
                                .setIdentityProvider(new AnonymousProvider())
                                .setRequestTimeout(UInteger.valueOf(5000))
                                .build()
        );
    }

    private static void subscribe(OpcUaClient client) throws Exception {
        //创建发布间隔1000ms的订阅对象
        client.getSubscriptionManager()
                .createSubscription(1000.0)
                .thenAccept(t -> {
                    //节点ns=2;s=test.device2.test2
//                    NodeId nodeId = new NodeId(4, 322);
                    NodeId nodeId = new NodeId(3, 1003);
                    ReadValueId readValueId = new ReadValueId(nodeId, AttributeId.Value.uid(), null, null);
                    //创建监控的参数
                    MonitoringParameters parameters = new MonitoringParameters(UInteger.valueOf(1), 1000.0, null, UInteger.valueOf(10), true);
                    //创建监控项请求
                    //该请求最后用于创建订阅。
                    MonitoredItemCreateRequest request = new MonitoredItemCreateRequest(readValueId, MonitoringMode.Reporting, parameters);
                    List<MonitoredItemCreateRequest> requests = new ArrayList<>();
                    requests.add(request);
                    //创建监控项，并且注册变量值改变时候的回调函数。
                    t.createMonitoredItems(
                            TimestampsToReturn.Both,
                            requests,
                            (item, id) -> item.setValueConsumer((it, val) -> {
                                System.out.println("=====订阅nodeid====== :" + it.getReadValueId().getNodeId());
                                System.out.println("=====订阅value===== :" + val.getValue().getValue());
                            })
                    );
                }).get();

        //持续订阅
        //Thread.sleep(Long.MAX_VALUE);
    }

    public static void readValue(OpcUaClient client) {
        try {
            NodeId nodeId = new NodeId(3, 1008);

            DataValue value = client.readValue(0.0, TimestampsToReturn.Both, nodeId).get();

            System.out.println("=====读取ua1====：" + value.getValue().getValue());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void writeValue(OpcUaClient client) {
        try {

            //创建变量节点 test.device2.test2
            NodeId nodeId = new NodeId(3, 1008);

            //uda3 boolean
            Short value = 11;
            //创建Variant对象和DataValue对象
            Variant v = new Variant(value);
            DataValue dataValue = new DataValue(v, null, null);

            StatusCode statusCode = client.writeValue(nodeId, dataValue).get();
            System.out.println(statusCode);
            System.out.println("=====写入ua1====：" + statusCode.isGood());

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}